package sq

import (
	"context"
	"fmt"
	"log"
	"reflect"
	"strconv"
)

var (
	mapper = NewReflectMapper("db")

	AutoCreateTimeFields = []string{ //插入数据库自动更新字段
		"create_time",
		"create_at",
		"created_at",
		"update_time",
		"update_at",
		"updated_at",
	}

	AutoUpdateTimeFields = []string{ //更新数据库自动更新字段
		"update_time",
		"update_at",
		"updated_at",
	}
)

type (
	ModelWrapper struct {
		dbList map[string]*DB
		model  interface{}
	}

	ModelWrapperFactory func(m interface{}) *ModelWrapper
)

func NewModelWrapper(dbList map[string]*DB, model interface{}) *ModelWrapper {
	return &ModelWrapper{dbList: dbList, model: model}
}

func (m *ModelWrapper) GetRelationDB(connect string) *DB {
	return m.dbList[connect]
}

func (m *ModelWrapper) UnWrap() interface{} {
	return m.model
}

// Model interface
type IModel interface {
	TableName() string
	PK() string
}

// Builder 模型结构体
type Builder struct {
	db      *DB
	model   interface{} //存数据的容器
	value   reflect.Value
	entity  IModel
	ctx     context.Context
	wrapper *ModelWrapper
	SqlBuilder
}

// Model 从结构体构造SQL
func Model(model interface{}) *Builder {
	return &Builder{
		model: model,
		db:    &DB{database: GetDB(defaultLink)},
	}
}

// Model construct SQL from Struct with context
func (b *Builder) Model(model interface{}) *Builder {
	b.model = model
	return b
}

// ShowSQL 输出单条SQL
func (b *Builder) ShowSQL() *Builder {
	b.db.logging = true
	return b
}

// Model construct SQL from Struct with context
func Ctx(ctx context.Context) *Builder {
	w := Use(defaultLink)
	return &Builder{db: w, SqlBuilder: SqlBuilder{dialect: newDialect(w.DriverName())}, ctx: ctx}
}

func (b *Builder) initModel() {
	if b.model == nil {
		log.Panicf("model argument must not nil")
	} else if m, ok := b.model.(IModel); ok {
		b.entity = m
		b.table = m.TableName()
		b.value = reflect.ValueOf(m)
		b.dialect = newDialect(b.db.DriverName())
	} else {
		value := reflect.ValueOf(b.model)
		if value.Kind() != reflect.Ptr {
			log.Panicf("model argument must pass a pointer, not a value %#v", b.model)
		}

		if value.IsNil() {
			log.Panicf("model argument cannot be nil pointer passed")
		}

		tp := reflect.Indirect(value).Type()

		if tp.Kind() == reflect.Interface {
			tp = reflect.Indirect(value).Elem().Type()
		}

		if tp.Kind() != reflect.Slice {
			log.Panicf("model argument must slice, but get %#v", b.model)
		}

		tpEl := tp.Elem()

		// Compatible with []*Struct or []Struct
		if tpEl.Kind() == reflect.Ptr {
			tpEl = tpEl.Elem()
		}

		if k, v := reflect.New(tpEl).Interface().(IModel); v {
			b.entity = k
			b.table = k.TableName()
			b.value = reflect.ValueOf(k)
			b.dialect = newDialect(b.db.DriverName())
		} else {
			log.Panicf("model argument must implementation IModel interface or slice []IModel and pointer,but get %#v", b.model)
		}
	}
}

// Hint is set TDDL "/*+TDDL:slave()*/"
func (b *Builder) Hint(hint string) *Builder {
	b.hint = hint
	return b
}

// ForceIndex
func (b *Builder) ForceIndex(i string) *Builder {
	b.forceIndex = i
	return b
}

// Where for example Where("id = ? and name = ?",1,"test")
func (b *Builder) Where(str string, args ...interface{}) *Builder {
	b.SqlBuilder.Where(str, args...)
	return b
}

// Cols filter column
func (b *Builder) Cols(fields string) *Builder {
	b.fields = fields
	return b
}

// Limit
func (b *Builder) Limit(i int) *Builder {
	b.limit = strconv.Itoa(i)
	return b
}

// Offset
func (b *Builder) Offset(i int) *Builder {
	b.offset = strconv.Itoa(i)
	return b
}

// OrderBy for example "id desc"
func (b *Builder) OrderBy(str string) *Builder {
	b.order = str
	return b
}

func (b *Builder) generateWhere(m map[string]interface{}) {
	for k, v := range m {
		b.Where(fmt.Sprintf("%s=?", k), v)
	}
}

func (b *Builder) generateWhereForPK(m map[string]interface{}) {
	pk := b.entity.PK()
	pval, has := m[pk]
	if b.where == "" && has {
		b.Where(fmt.Sprintf("%s=?", pk), pval)
		delete(m, pk)
	}
}

func (b *Builder) reflectModel(autoTime []string) map[string]reflect.Value {
	fields := mapper.FieldMap(b.value)
	if autoTime != nil {
		structAutoTime(fields, autoTime)
	}
	return fields
}

//Count sq.Model(&User{}).Where("status = 0").Count()
func (b *Builder) Count(zeroValues ...string) (num int64, err error) {
	b.initModel()

	m := zeroValueFilter(b.reflectModel(nil), zeroValues)
	// If where is empty, the primary key where condition is generated automatically
	b.generateWhere(m)

	err = b.db.Take(&num, b.countString(), b.args...)
	return num, err
}

// Relation 关联表构建器句柄
func (b *Builder) Relation(fieldName string, fn BuildFunc) *Builder {
	if b.db.RelationMap == nil {
		b.db.RelationMap = make(map[string]BuildFunc)
	}
	b.db.RelationMap[fieldName] = fn
	return b
}

// Insert 插入一条记录。
//  例如: sq.Model(&User{Id:1}).Insert()
func (b *Builder) Insert() (lastInsertId int64, err error) {
	b.initModel()
	hook := NewHook(b.ctx, b.db)
	hook.callMethod("BeforeChange", b.value)
	hook.callMethod("BeforeCreate", b.value)
	if hook.HasError() {
		return 0, hook.Error()
	}

	fields := b.reflectModel(AutoCreateTimeFields)
	m := structToMap(fields)

	result, err := b.db.Exec(b.insertString(m), b.args...)
	if err != nil {
		return 0, err
	}

	hook.callMethod("AfterCreate", b.value)
	hook.callMethod("AfterChange", b.value)

	if hook.HasError() {
		return 0, hook.Error()
	}

	lastId, err := result.LastInsertId()

	if err != nil {
		return 0, err
	}

	if v, ok := fields[b.entity.PK()]; ok {
		fillPrimaryKey(v, lastId)
	}

	return lastId, err
}

// Delete 删除一条记录。参数<zeroValues>即使是0值，也会被强制执行。
//  例如: sq.Model(&User{Id:1}).Delete()。
func (b *Builder) Delete(zeroValues ...string) (affected int64, err error) {
	b.initModel()
	hook := NewHook(b.ctx, b.db)
	hook.callMethod("BeforeChange", b.value)
	hook.callMethod("BeforeDelete", b.value)
	if hook.HasError() {
		return 0, hook.Error()
	}

	m := zeroValueFilter(b.reflectModel(nil), zeroValues)
	// If where is empty, the primary key where condition is generated automatically
	b.generateWhere(m)

	result, err := b.db.Exec(b.deleteString(), b.args...)
	if err != nil {
		return 0, err
	}

	hook.callMethod("AfterDelete", b.value)
	hook.callMethod("AfterChange", b.value)

	if hook.HasError() {
		return 0, hook.Error()
	}

	return result.RowsAffected()
}

// Update 更新一条记录。参数<zeroValues>即使是0值，也会被强制执行。
//  例如: sq.Model(&User{Id:1,Status:0}).Update("status")
func (b *Builder) Update(zeroValues ...string) (affected int64, err error) {
	b.initModel()
	hook := NewHook(b.ctx, b.db)
	hook.callMethod("BeforeChange", b.value)
	hook.callMethod("BeforeUpdate", b.value)
	if hook.HasError() {
		return 0, hook.Error()
	}

	fields := b.reflectModel(AutoUpdateTimeFields)
	m := zeroValueFilter(fields, zeroValues)

	// If where is empty, the primary key where condition is generated automatically
	b.generateWhereForPK(m)

	result, err := b.db.Exec(b.updateString(m), b.args...)
	if err != nil {
		return 0, err
	}

	hook.callMethod("AfterUpdate", b.value)
	hook.callMethod("AfterChange", b.value)

	if hook.HasError() {
		return 0, hook.Error()
	}

	return result.RowsAffected()
}

// Take 查询一条记录。参数<zeroValues>即使是0值，也会被强制执行。
//  例如: user := &Users{Id:1,Status:0}
//       sq.Model(&user).Take("status")
func (b *Builder) Take(zeroValues ...string) (err error) {
	b.initModel()
	m := zeroValueFilter(b.reflectModel(nil), zeroValues)
	// 如果where为空，则自动生成条件的主键
	b.generateWhere(m)

	if b.wrapper != nil {
		return b.db.Take(b.wrapper, b.queryString(), b.args...)
	}
	return b.db.Take(b.model, b.queryString(), b.args...)
}

// Select 查询多条记录。
func (b *Builder) Select() (err error) {
	b.initModel()
	if b.wrapper != nil {
		return b.db.Select(b.wrapper, b.queryString(), b.args...)
	}
	return b.db.Select(b.model, b.queryString(), b.args...)
}


